// ==UserScript==
// @name         autoex
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  try to take over the world!
// @author       TwoDogXu
// @match        https://oa.9pan.cc/
// @icon         https://www.google.com/s2/favicons?domain=9pan.cc
// @grant        GM_xmlhttpRequest
// @grant        GM_download

// ==/UserScript==
console.log("进入脚本");

(function () {
  "use strict";
  bindButton(2000);
})();

// 加载按钮
function bindButton(time) {
  // 首页进入添加按钮
  setTimeout(bindButtonStep1, time);
  // 已在数据列表页面添加按钮
  try {
    setTimeout(addButton, time);
  } catch (err) {
    console.log(err);
  }
}

function bindButtonStep1() {
  var x = document.querySelector(
    "#app > div > div.sidebar > ul > li:nth-child(2) > div"
  );
  x.onclick = function () {
    setTimeout(bindButtonStep2, 500);
  };
}

function bindButtonStep2() {
  var x = document.querySelector(
    "#app > div > div.sidebar > ul > li.el-submenu.is-opened > ul > li > ul > li:nth-child(2)"
  );
  x.onclick = function () {
    setTimeout(addButton, 500);
  };
}

function addButton() {
  var x = document.getElementsByClassName("MenuManagementTop1")[0];
  // 避免重复添加按键
  if (x.getElementsByTagName("ex-button").length > 0) {
    return;
  }
  console.log("准备添加按钮");
  var button = document.createElement("ex-button"); //创建一个input对象（提示框按钮）
  button.id = "id001";
  button.textContent = "导出";
  button.style.width = "73px";
  button.style.height = "32px";
  button.style.align = "center";
  button.style.textAlign = "center";
  button.style.backgroundColor = "#408eff";

  //绑定按键点击功能
  button.onclick = function () {
    console.log("点击了按键");
    //为所欲为 功能实现处
    spider();
    return;
  };

  //在浏览器控制台可以查看所有函数，ctrl+shift+I 调出控制台，在Console窗口进行实验测试
  x.appendChild(button);
  // 添加状态
  addStatus();
  // 添加输出
  addOutput();
}

function addStatus(){
  var x = document.getElementsByClassName("MenuManagementTop1")[0];
  // 避免重复添加按键
  if (x.getElementsByTagName("ex-status").length > 0) {
    return;
  }
  console.log("添加status");
  var status = document.createElement("ex-status"); //创建一个input对象（提示框按钮）
  status.id = "output_01";
  status.textContent = "- -";
  status.style.width = "73px";
  status.style.height = "32px";
  status.style.align = "center";
  status.style.textAlign = "center";
  x.appendChild(status);
}

function setStatus(text){
  var x = document.getElementsByClassName("MenuManagementTop1")[0];
  if (x.getElementsByTagName("ex-status").length == 1) {
    var status = x.getElementsByTagName("ex-status")[0];
    status.textContent = text;
  }
}

function addOutput(){
  var x = document.getElementsByClassName("MenuManagementTop1")[0];
  // 避免重复添加按键
  if (x.getElementsByTagName("ex-output").length > 0) {
    return;
  }
  console.log("准备添加按钮");
  var output = document.createElement("ex-output"); //创建一个input对象（提示框按钮）
  output.id = "output_01";
  output.textContent = "0/0";
  output.style.width = "73px";
  output.style.height = "32px";
  output.style.align = "center";
  output.style.textAlign = "center";
  // output.style.backgroundColor = "#408eff";
  x.appendChild(output);
}

function setOutput(text){
  var x = document.getElementsByClassName("MenuManagementTop1")[0];
  if (x.getElementsByTagName("ex-output").length == 1) {
    var output = x.getElementsByTagName("ex-output")[0];
    output.textContent = text;
  }
}

function spider() {
  let start = Date.now();
  letsFuck()
    .then(exportCsv)
    .then(letsFuckUnCallUsers)
    .then(() => console.info("耗时: ", (Date.now() - start) / 1000))
    .then(() => setStatus("完成"))
    .then(() => setOutput("left: 0"))
    .then(() => location.reload());
}

function delay(asyncTask, waiting) {
  const sleep = (millisec, payload) =>
    new Promise((resolve) => setTimeout(() => resolve(payload), millisec));

  let start = Date.now();
  return asyncTask().then((res) => {
    let rest = waiting - (Date.now() - start);
    if (rest > 0) {
      console.log("rest:", rest);
      return sleep(rest, res);
    }

    return res;
  });
}

/**
 * @param {Array<Promise>} queue 异步队列
 * @param {number} [parallel] 并发数
 * @param {number} [waiting] 时间间隔
 */
async function parallelThrottle(queue, parallel = 2, waiting = 1000) {
  const results = [];

  while (queue.length) {
    console.log(queue.length);
    setOutput("left:" + queue.length)
    const parallelTasks = queue.splice(0, parallel);
    const res = await delay(
      () => Promise.all(parallelTasks.map((cb) => cb())),
      waiting
    );
    results.push(...res);
  }

  return results;
}

function post(url, payload, retry = 0) {
  const RETRY_MAX_TIMES = 3;

  let body = undefined;
  if (payload) {
    body = new FormData();
    Object.entries(payload).forEach(([key, value]) => {
      body.append(key, value);
    });
  }

  return fetch(url, {
    method: "POST",
    mode: "cors",
    credentials: "include",
    body,
  })
    .then((res) => res.json())
    .then((res) => {
      if (!res.data){
        if(res.msg) setOutput(res.msg);
        throw new Error("nil data");
      } 
      return res.data;
    })
    .catch((e) => {
      if (retry >= RETRY_MAX_TIMES) throw e;

      console.error(e);
      return post(url, payload, (retry || 0) + 1);
    });
}

function fetchListByPage(page) {
  return post("https://api.9pan.cc/oa/pool/list", { page, status4: 0 }); // 状态固定？
}

function fetchMobileById(id) {
  return post("https://api.9pan.cc/oa/recall/mobile", { id }).then(
    (res) => res.mobile
  );
}

function commitStatus(id, status4){
  return post("https://api.9pan.cc/oa/recall/commit", { id, status4})
}

function getPagination() {
  return fetchListByPage(1).then(({ lastPage }) =>
    Array.from({ length: lastPage }, (_, i) => i + 1)
  );
}

async function letsFuck() {
  try {
    setStatus("获取列表:");
    const pagination = await getPagination();
    const data = await parallelThrottle(
      pagination.map((page) => () => fetchListByPage(page))
    );

    setStatus("获取手机号:")
    const list = data.map((d) => d.items).flat();
    const mobiles = await parallelThrottle(
      list.map((r) => () => fetchMobileById(r.id)),
      3, 5000 // 获取手机号的并发降低
    );

    return list.map((res, idx) => ({
      ...res,
      phoneNumber: mobiles[idx],
    }));
  } catch (error) {
    console.error(error);
  }
}

const CSV_HEADER = {
  no: "Account",
  phoneNumber: "Phone",
};

function exportCsv(data, filename = "export.csv") {
  console.log("[exportCsv]", data);

  const csv = [Object.values(CSV_HEADER)];
  const headerKeys = Object.keys(CSV_HEADER);
  data.forEach((d) => {
    csv.push(headerKeys.map((key) => d[key]));
  });
  const blob = new Blob([csv.join("\n")], { type: "text/csv" });

  const a = document.createElement("a");
  a.download = filename;
  a.href = window.URL.createObjectURL(blob);
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);

  return data;
}

async function letsFuckUnCallUsers(users){
  console.log("now funck uncall users!");
  try{
    setStatus("召回中:")
    const data = await parallelThrottle(
      users.map((user) => () => commitStatus(user.id, 2)),
      3, 5000 // 标记为已召回 降低并发
    );
  } catch (error) {
    console.error(error);
  }
}
